using System;
using System.Collections.Generic;
using System.Linq;
using WinForms = System.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Reflection;
using System.Reflection.Emit;
using SmokinAces.Object_Classes;
using SmokinAces.Object_Classes.Objects;
using SmokinAces;
namespace SmokinEditor
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game_Editor : SmokinAces.Main_Engine
    {
        public WinForms.Form FormHandle;
        Editor_Tools ToolContainer;
        Game_Object Drag_Object = null;
        //-------------------------------
        bool selecting = false;
        bool bounds = false;

        Vector2 StartSelectionPoint = Vector2.Zero;
        BoundingBox Selection_Box = new BoundingBox(Vector3.Zero, Vector3.One);
        HashSet<Game_Object> SelectedObjects = new HashSet<Game_Object>();
        //-------------------------------
        //-------------------This is the code used for Drag Drop
        private void DragOver(Object sender, WinForms.DragEventArgs drgevent)
        {
            if (drgevent.Data.GetDataPresent(WinForms.DataFormats.Serializable))
            {
                FormHandle.AllowDrop = true;
                object tempOb = drgevent.Data.GetData(WinForms.DataFormats.Serializable);
                if(tempOb is Type)
                {
                    Type drawer = tempOb as Type;
                    Type[] parms = new Type[3];
                    parms[0] = typeof(int);
                    parms[1] = typeof(int);
                    parms[2] = typeof(Main_Engine);
                    ConstructorInfo ci = drawer.GetConstructor(parms);
                    object[] cparms = new object[3];
                    cparms[0] = ((int)this.CurrentScreen.Min.X + (drgevent.X - FormHandle.Left));
                    cparms[1] = ((int)this.CurrentScreen.Max.Y - (drgevent.Y - FormHandle.Top));
                    cparms[2] = ((Main_Engine)this);
                    Drag_Object = ((Game_Object)ci.Invoke(cparms));
                    this.BeginDraw();
                }
            }
        }

        private void FrmMouseUp(Object sender, WinForms.MouseEventArgs msevent)
        {
            if (Drag_Object != null && (ToolContainer.SelectedObject.Name != "Selection"))
            {
                if (Drag_Object is Main_Character)
                {
                    Hero = (Main_Character)Drag_Object;
                    Drag_Object = null;// new Main_Character((int)Hero.GraphicBB.Min.X, (int)Hero.GraphicBB.Min.Y, this);
                }
                else
                {
                    MapObjects.Add(Drag_Object);
                    Drag_Object = null;
                }
            }
        }
        //------------------------------------------------------

        GraphicsDeviceManager graphics;
        public SpriteBatch spriteBatch;
        VertexDeclaration VertexColorTexture;
        VertexDeclaration VertexPosColor;
        Matrix viewMatrix;
        Matrix projectionMatrix;

        public Game_Editor()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            int LayerCount = Enum.GetNames(typeof(Layers)).Length;
            Draw_Functions = new List<Sort_Delegate>[LayerCount];
            for (int i = 0; i < LayerCount; i++)
            {
                Draw_Functions[i] = new List<Sort_Delegate>();
            }
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
       { 
            //----------------Initialize our Layers--------------
            //----------------Adding a new Layer to the layers enum propogates here.
            int LayerCount = Enum.GetNames(typeof(Layers)).Length;
            Drawing_List = new Dictionary<string, Texture_List>[LayerCount];
            for (int i = 0; i < LayerCount; i++)
            {
                Drawing_List[i] = new Dictionary<string, Texture_List>();
            }
            //---------------------------------------------------
            graphics.PreferredBackBufferWidth = 1024;
            graphics.PreferredBackBufferHeight = 768;
            graphics.IsFullScreen = false;
            graphics.ApplyChanges();

            Window.Title = "Smoking Aces: The Adventure Game";
            this.IsMouseVisible = true;
            FormHandle = (WinForms.Form)WinForms.Form.FromHandle(Window.Handle);
            FormHandle.AllowDrop = true;
            FormHandle.DragOver += new WinForms.DragEventHandler(DragOver);
            FormHandle.MouseUp += new WinForms.MouseEventHandler(FrmMouseUp);
            FormHandle.LostFocus += new EventHandler(FormHandle_LostFocus);
            FormHandle.GotFocus += new EventHandler(FormHandle_GotFocus);
            graphics.DeviceReset += new EventHandler(graphics_DeviceReset);
            base.Initialize();
       }

        void graphics_DeviceReset(object sender, EventArgs e)
        {
            GraphicsDevice.RenderState.AlphaBlendEnable = true;
            GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
        }

        void FormHandle_GotFocus(object sender, EventArgs e)
        {
            focus = true;
        }

        void FormHandle_LostFocus(object sender, EventArgs e)
        {
            focus = false;
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            //Initialize the audio stuff
            SoundManager.Initialize(this);

            // TODO: use this.Content to load your game content here
            viewMatrix = Matrix.CreateLookAt(new Vector3(GraphicsDevice.Viewport.Width / 2.0f, GraphicsDevice.Viewport.Height / 2.0f, 927), new Vector3(GraphicsDevice.Viewport.Width / 2.0f, GraphicsDevice.Viewport.Height / 2.0f, 0.0f), Vector3.Up);
            //projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1.0f, 1024.0f);
            projectionMatrix = Matrix.CreateOrthographicOffCenter(CurrentScreen.Min.X, CurrentScreen.Max.X, CurrentScreen.Min.Y, CurrentScreen.Max.Y, 0.0f, 1024.0f);

            //.CreateOrthographic(1024.0f, 768.0f, 0.0f, 1024.0f);
            VertexColorTexture = new VertexDeclaration(GraphicsDevice, VertexPositionColorTexture.VertexElements);
            VertexPosColor = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);
            //----------Build the Quadtree
            BoundingBox MapSize = new BoundingBox(new Vector3(0, 0, -1000.0f), new Vector3(6400, 6400, 1000.0f));
            MapObjects = new Quadtree(MapSize);
            HashSet<Game_Object> TempList = MapObjects.Intersections(CurrentScreen);
            foreach (Game_Object iter in TempList)
            {
                //---Only Start Drawing if the remove succeeded and it's a drawing object
                if (iter is Draw_Object)
                {
                    (iter as Draw_Object).startDraw();
                }
                if (iter is Logic_Object)
                {
                    (iter as Logic_Object).startUpdate();
                }
            }
            GameObjects = TempList.ToList<Game_Object>();
            //----------------------------
            //effect = this.Content.Load<Effect>("effects");
            effect = new Game_Effect(this); //new BasicEffect(this.GraphicsDevice,null);
            effect.EnableTinting();
            LineEffect = new BasicEffect(this.GraphicsDevice,null);
            //LineEffect.CurrentTechnique = LineEffect.Techniques["colored"];
            LineEffect.VertexColorEnabled = true;
            LineEffect.TextureEnabled = false;
            GraphicsDevice.RenderState.AlphaBlendEnable = true;
            GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
            ToolContainer = new Editor_Tools(this);
            ToolContainer.Show();
            ScrollValue = Mouse.GetState().ScrollWheelValue;

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            ToolContainer.Close();
            // TODO: Unload any non ContentManager content here
        }
        private static bool IsEngineSim(object type)
        {
            Type blah = type as Type;
            return blah.Name == "Main_Engine";
        }
        int ScrollValue = 0;
        private void MouseUpdate()
        {
            MouseState mouseState = Mouse.GetState();
            if (mouseState.ScrollWheelValue != ScrollValue)
            {
                int scrooll = (mouseState.ScrollWheelValue - ScrollValue);
                ScrollValue = mouseState.ScrollWheelValue;
                if (Keyboard.GetState().IsKeyDown(Keys.RightControl) || Keyboard.GetState().IsKeyDown(Keys.LeftControl))
                {
                    CurrentScreen.Min.X -= (scrooll / 120) * 32;
                    CurrentScreen.Max.X -= (scrooll / 120) * 32;
                }
                else
                {
                    CurrentScreen.Min.Y += (scrooll / 120) * 32;
                    CurrentScreen.Max.Y += (scrooll / 120) * 32;

                }
            }

            if (ToolContainer.SelectedObject != null)
            {
                #region Selection Code
                if (ToolContainer.SelectedObject.Name == "Selection")
                {
                    if (mouseState.LeftButton == ButtonState.Pressed)
                    {
                        if (selecting == false)
                        {
                            if (!Keyboard.GetState().IsKeyDown(Keys.LeftControl))
                            {
                                SelectedObjects.Clear();
                            }
                            StartSelectionPoint.X = ((int)this.CurrentScreen.Min.X + (mouseState.X));
                            StartSelectionPoint.Y = ((int)this.CurrentScreen.Max.Y - (mouseState.Y));
                            selecting = true;
                        }
                        Vector2 CurrentSelectionPoint = new Vector2();
                        CurrentSelectionPoint.X = ((int)this.CurrentScreen.Min.X + (mouseState.X));
                        CurrentSelectionPoint.Y = ((int)this.CurrentScreen.Max.Y - (mouseState.Y));
                        Vector2 minPoint = Vector2.Min(StartSelectionPoint, CurrentSelectionPoint);
                        Vector2 maxPoint = Vector2.Max(StartSelectionPoint, CurrentSelectionPoint);
                        Selection_Box.Min = new Vector3(minPoint.X, minPoint.Y, -1000.0f);
                        Selection_Box.Max = new Vector3(maxPoint.X, maxPoint.Y, 1000.0f);
                    }
                    else if (mouseState.LeftButton == ButtonState.Released)
                    {
                        if (selecting == true && focus)
                        {
                            selecting = false;
                            HashSet<Game_Object> TempList = MapObjects.Intersections(Selection_Box);
                            foreach (Game_Object iter in TempList)
                            {
                                if (iter.GraphicBB.Intersects(Selection_Box))
                                {
                                    SelectedObjects.Add(iter);
                                }
                            }
                            if (SelectedObjects.Count <= 0)
                            {
                                return;
                            }
                            IEnumerable<Map_Tile> OnlyMapTiles = SelectedObjects.OfType<Map_Tile>();
                            List<Map_Tile> MapTileList = OnlyMapTiles.ToList();
                            switch ((SelectionOptions)(ToolContainer.TheProperties.Blast.default_const_values[0]))
                            {
                                case SelectionOptions.Normal:
                                    //Remove all of the map tiles
                                    foreach (Map_Tile MapTileObj in MapTileList)
                                    {
                                        SelectedObjects.Remove(MapTileObj);
                                    }
                                    ToolContainer.PropertyChanger.SelectedObjects = SelectedObjects.ToArray();

                                    break;
                                case SelectionOptions.MapTiles:
                                    //SelectedObjects.Clear();
                                    HashSet<Game_Object> tempHash = new HashSet<Game_Object>();
                                    ToolContainer.PropertyChanger.SelectedObjects = tempHash.ToArray();
                                    foreach (Map_Tile MapTileObj in MapTileList)
                                    {
                                        tempHash.Add(MapTileObj);
                                    }
                                    SelectedObjects = tempHash;
                                    ToolContainer.PropertyChanger.SelectedObjects = SelectedObjects.ToArray();
                                    break;
                            }
                        }
                    }
                }
                #endregion
                else
                {
                    int GameEngineTypeIndex = ToolContainer.TheProperties.Blast.constructor_parameters.ToList().FindIndex(IsEngineSim);
                    ToolContainer.TheProperties.Blast.default_const_values[GameEngineTypeIndex] = this;

                    ToolContainer.TheProperties.Blast.default_const_values[0] = ((int)this.CurrentScreen.Min.X + (mouseState.X));
                    ToolContainer.TheProperties.Blast.default_const_values[1] = ((int)this.CurrentScreen.Max.Y - (mouseState.Y));
                    ConstructorInfo ci = ToolContainer.SelectedObject.GetConstructor(ToolContainer.TheProperties.Blast.constructor_parameters);
                    Drag_Object = ((Game_Object)ci.Invoke(ToolContainer.TheProperties.Blast.default_const_values));
                }
            }
        }
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        bool focus;
        protected override void Update(GameTime gameTime)
        {

            MouseUpdate();
            if (focus)
            {
            KeyboardState keyState = Keyboard.GetState();
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            if (keyState.IsKeyDown(Keys.Escape))
            {
                SoundManager.FadeMusic();
                Exit();
            }
            if (keyState.IsKeyDown(Keys.Multiply))
            {
                bounds = !bounds;
            }
            if (keyState.IsKeyDown(Keys.Left))
            {
                CurrentScreen.Min.X -= 32;
                CurrentScreen.Max.X -= 32;
            }
            if (keyState.IsKeyDown(Keys.Right))
            {
                CurrentScreen.Min.X += 32;
                CurrentScreen.Max.X += 32;
            }
            if (keyState.IsKeyDown(Keys.Up))
            {
                CurrentScreen.Min.Y += 32;
                CurrentScreen.Max.Y += 32;
            }
            if (keyState.IsKeyDown(Keys.Down))
            {
                CurrentScreen.Min.Y -= 32;
                CurrentScreen.Max.Y -= 32;
            }
            if (keyState.IsKeyDown(Keys.Delete))
            {
                //Delete the selected objects!
                foreach (Game_Object iter in SelectedObjects)
                {
                    if (!(iter is Map_Tile))
                    {
                        MapObjects.Remove(iter);
                    }
                }
            }
            // TODO: Add your update logic here
            foreach (Update_Delegate iter in UpdateObjects)
            {
                iter.Invoke(gameTime);
            }
            }

            int LayerCount = Enum.GetNames(typeof(Layers)).Length;
            for (int i = 0; i < LayerCount; i++)
            {
                Draw_Functions[i].Clear();
            }
            HashSet<Game_Object> TempList = MapObjects.Intersections(CurrentScreen);
            foreach (Game_Object iter in TempList)
            {
                //---Only Start Drawing if the remove succeeded and it's a drawing object
                if (iter is Draw_Object)
                {
                    (iter as Draw_Object).startDraw();
                }
            }
            if (Hero != null)
            {
                Hero.startDraw();
            }
            GameObjects = TempList.ToList<Game_Object>();
            base.Update(gameTime);
        }
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Red);
            projectionMatrix = Matrix.CreateOrthographicOffCenter(CurrentScreen.Min.X - 512, CurrentScreen.Max.X - 512, CurrentScreen.Min.Y - 384, CurrentScreen.Max.Y - 384, 0.0f, 1024.0f);

            GraphicsDevice.RenderState.CullMode = CullMode.None;
            //effect.EnableTexture = true;
            //effect.VertexColorEnabled = true;
            LineEffect.Projection = projectionMatrix;
            LineEffect.View = viewMatrix;
            LineEffect.World = Matrix.Identity;
            Game_Effect.Projection = projectionMatrix;
            Game_Effect.View = viewMatrix;
            Game_Effect.World = Matrix.Identity;
            GraphicsDevice.VertexDeclaration = VertexColorTexture;
            int LayerCount = Enum.GetNames(typeof(Layers)).Length;
            for (int i = 0; i < LayerCount; i++)
            {
                Draw_Functions[i].Sort();
                foreach (Sort_Delegate iter in Draw_Functions[i])
                {
                    iter.draw(gameTime, GraphicsDevice);
                }
            }
            if (Drag_Object != null)
            {
                if (Drag_Object is Draw_Object)
                {
                    (Drag_Object as Draw_Object).Draw(gameTime, GraphicsDevice);
                }
            }


            GraphicsDevice.VertexDeclaration = VertexPosColor;
            //---DrawLines
            foreach(Game_Object iter in SelectedObjects)
            {
                Draw_Lines(iter.GraphicBB);
            }
            if (selecting == true)
            {
                Draw_Lines(Selection_Box);
            }

            //----------Draw Bounds
            if (bounds)
            {
                GraphicsDevice.VertexDeclaration = VertexPosColor;

                foreach (Game_Object iter in GameObjects)
                {
                    SelectionColor = Color.White;
                    foreach (BoundingBox iter2 in iter.Bounding_Regions)
                    {
                        Draw_Lines(iter2);
                    }
                    SelectionColor = Color.Red;
                    foreach (BoundingBox iter2 in iter.Bounding_Weapon)
                    {
                        Draw_Lines(iter2);
                    }
                }


                SelectionColor = Color.White;
                foreach (BoundingBox iter2 in Hero.Bounding_Regions)
                {
                    Draw_Lines(iter2);
                }
                SelectionColor = Color.Red;
                foreach (BoundingBox iter2 in Hero.Bounding_Weapon)
                {
                    Draw_Lines(iter2);
                }
            }
            base.Draw(gameTime);
        }


    }
}
